// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.linqingying.gitcode.pullrequest.data.provider

import com.intellij.collaboration.util.CollectionDelta
import com.intellij.openapi.Disposable
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.util.Disposer
import com.intellij.platform.util.coroutines.childScope
import com.intellij.util.concurrency.annotations.RequiresEdt
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.future.asDeferred
import com.linqingying.gitcode.api.data.GCLabel
import com.linqingying.gitcode.api.data.GCUser
import com.linqingying.gitcode.api.data.pullrequest.GCPullRequest
import com.linqingying.gitcode.api.data.pullrequest.GCPullRequestRequestedReviewer
import java.util.concurrent.CompletableFuture

interface GCPRDetailsDataProvider {

  val loadedDetails: GCPullRequest?

  @RequiresEdt
  fun loadDetails(): CompletableFuture<GCPullRequest>

  @RequiresEdt
  fun reloadDetails()

  @RequiresEdt
  fun addDetailsReloadListener(disposable: Disposable, listener: () -> Unit)

  @RequiresEdt
  fun loadDetails(disposable: Disposable, consumer: (CompletableFuture<GCPullRequest>) -> Unit) {
    addDetailsReloadListener(disposable) {
      consumer(loadDetails())
    }
    consumer(loadDetails())
  }

  @RequiresEdt
  fun addDetailsLoadedListener(disposable: Disposable, listener: () -> Unit)

  @RequiresEdt
  fun updateDetails(indicator: ProgressIndicator, title: String? = null, description: String? = null): CompletableFuture<GCPullRequest>

  @RequiresEdt
  fun adjustReviewers(indicator: ProgressIndicator, delta: CollectionDelta<GCPullRequestRequestedReviewer>)
    : CompletableFuture<Unit>

  @RequiresEdt
  fun adjustAssignees(indicator: ProgressIndicator, delta: CollectionDelta<GCUser>)
    : CompletableFuture<Unit>

  @RequiresEdt
  fun adjustLabels(indicator: ProgressIndicator, delta: CollectionDelta<GCLabel>)
    : CompletableFuture<Unit>
}

fun GCPRDetailsDataProvider.detailsRequestFlow(): Flow<Deferred<GCPullRequest>> =
  channelFlow {
    val listenerDisposable = Disposer.newDisposable()
    val cs = childScope()
    loadDetails(listenerDisposable) { request ->
      cs.async {
        try {
          request.asDeferred().await()
        }
        catch (e: ProcessCanceledException) {
          cancel()
          awaitCancellation()
        }
      }.let {
        trySend(it)
      }
    }
    awaitClose {
      Disposer.dispose(listenerDisposable)
    }
  }.flowOn(Dispatchers.Main)
